/*
INA226.cpp - Class file for the INA226 Bi-directional Current/Power Monitor
Arduino Library.

Version: 1.0.0
(c) 2014 Korneliusz Jarzebski
www.jarzebski.pl

This program is free software: you can redistribute it and/or modify
it under the terms of the version 3 GNU General Public License as
published by the Free Software Foundation.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program.  If not, see <http://www.gnu.org/licenses/>.
*/

#include "INA226.h"

#include <math.h>

#include "../c3log.h"

#define LOG_TAG "Ina226A"

int INA226::beginProbe(I2cInterface I2cIntf) {
    mI2cIntf = I2cIntf;
    mI2cIntf.i2c_master_init();
    int ret = mI2cIntf.I2cScannerAddr();
    if (ret != 0) {
        LOGE("%s scan and probe error", __func__);
    }
    return ret;
}

int INA226::configure(ina226_averages_t avg, ina226_busConvTime_t busConvTime,
                       ina226_shuntConvTime_t shuntConvTime,
                       ina226_mode_t mode) {
    uint16_t config = 0;

    config |= (avg << 9 | busConvTime << 6 | shuntConvTime << 3 | mode);

    vBusMax = 36;  // 0xffff * 1.25mV 计算得到，能测得最大电压
    vShuntMax = 0.08192f;  // 0xffff * 0.25uV 计算得到，能测得最大电压

    int ret = mI2cIntf.writeRegister16(INA226_REG_CONFIG, config);
    if (ret != 0) {
        LOGE("%s writeRegister16 INA226_REG_CONFIG fail ret:%d", __func__, ret);
        return ret;
    }
    return ret;
}

int INA226::calibrate(float rShuntValue, float iMaxExpected) {
    int ret = 0;
    uint16_t calibrationValue;
    rShunt = rShuntValue;
    float iMaxPossible = vShuntMax / rShunt;
    float minimumLSB = iMaxExpected / 32767;
    currentLSB = (uint16_t)(minimumLSB * 100000000);
    currentLSB /= 100000000;
    currentLSB /= 0.0001;
    currentLSB = ceil(currentLSB);
    currentLSB *= 0.0001;

    // my iMaxExpected
    currentLSB = iMaxExpected / 0x8000;

    powerLSB = currentLSB * 25;
    calibrationValue = (uint16_t)((0.00512) / (currentLSB * rShunt));

    uint16_t read_v = mI2cIntf.readRegister16(INA226_REG_CALIBRATION);
    LOGI("calibrationValue read2 :%d", read_v);

    ret = mI2cIntf.writeRegister16(INA226_REG_CALIBRATION, calibrationValue);
    if (0 != ret) {
        LOGI("%s writeRegister16 fail ret:%d", __func__, ret);
        return ret;
    }
    LOGI("calibrationValue:%d", calibrationValue);
    uint16_t read_v2 = mI2cIntf.readRegister16(INA226_REG_CALIBRATION);
    if (read_v2 < 0) {
        LOGI("%s readRegister16 INA226_REG_CALIBRATION fail ret:%d", __func__, read_v2);
        return read_v2;
    }
    LOGI("calibrationValue read2 :%d", read_v2);
    return 0;
}

float INA226::getMaxPossibleCurrent(void) { return (vShuntMax / rShunt); }

float INA226::getMaxCurrent(void) {
    float maxCurrent = (currentLSB * 32767);
    float maxPossible = getMaxPossibleCurrent();

    if (maxCurrent > maxPossible) {
        return maxPossible;
    } else {
        return maxCurrent;
    }
}

float INA226::getMaxShuntVoltage(void) {
    float maxVoltage = getMaxCurrent() * rShunt;
    if (maxVoltage >= vShuntMax) {
        return vShuntMax;
    } else {
        return maxVoltage;
    }
}

float INA226::getMaxPower(void) { return (getMaxCurrent() * vBusMax); }

float INA226::readBusPower(void) {
    // mult 25mW
    return (mI2cIntf.readRegister16(INA226_REG_POWER) *
            powerLSB);  // Power = Current * BusVoltage/20,000
}

float INA226::readShuntCurrent(void) {
    int16_t val = mI2cIntf.readRegister16(INA226_REG_CURRENT);
    // LOGI("read reg: current:%d", val); // mult , shunt R = ?100mR
    if (val < 0) {
        LOGE("%s read current failed", __func__);
        val = 0;
    }
    return (val *
            currentLSB);  // Current = ShuntVoltage * CalibrationRegister / 2048
}

double INA226::readShuntVoltage(void) {
    float voltage = mI2cIntf.readRegister16(INA226_REG_SHUNTVOLTAGE);
    return (voltage * 0.0000025);  // mult 2.5uV
}

float INA226::readBusVoltage(void) {
    int16_t voltage = mI2cIntf.readRegister16(INA226_REG_BUSVOLTAGE);
    if (voltage < 0) {
        LOGE("%s read current failed, voltage:%f", __func__, voltage * 0.00125);
        voltage = 0;
    }
    return (voltage * 0.00125);  // mult 1.25 mV
}

int INA226::fastReadIV(int16_t &mI, int16_t &mV) {
    uint8_t v[4] = {0, 0, 0, 0};
    int ret = 0;
#if 0
    ret = mI2cIntf.readRegN(INA226_REG_BUSVOLTAGE, INA226_REG_SHUNTVOLTAGE, v, sizeof(v));
    mV = (v[0] << 8 | v[1]);

    ret = mI2cIntf.readRegN(INA226_REG_SHUNTVOLTAGE, INA226_REG_BUSVOLTAGE, v, sizeof(v));
    mI = (v[0] << 8 | v[1]);
    // LOGI("out:%d,%d", (int)mI, (int)mV);
#endif
    mV = mI2cIntf.readRegister16(INA226_REG_BUSVOLTAGE);
    static int cnt = 0, premI = 0.0;
    const int FPS = 3;
    if ((cnt == 0) || (cnt == 1000/FPS)) {
        cnt = 0;
        mI = mI2cIntf.readRegister16(INA226_REG_SHUNTVOLTAGE);
        premI = mI;
    } else {
        mI = premI;
    }
    cnt++;
    return 0;  // mult 1.25 mV
}

ina226_averages_t INA226::getAverages(void) {
    uint16_t value;
    value = mI2cIntf.readRegister16(INA226_REG_CONFIG);
    value &= 0b0000111000000000;
    value >>= 9;
    return (ina226_averages_t)value;
}

ina226_busConvTime_t INA226::getBusConversionTime(void) {
    uint16_t value;
    value = mI2cIntf.readRegister16(INA226_REG_CONFIG);
    value &= 0b0000000111000000;
    value >>= 6;

    return (ina226_busConvTime_t)value;
}

ina226_shuntConvTime_t INA226::getShuntConversionTime(void) {
    uint16_t value;
    value = mI2cIntf.readRegister16(INA226_REG_CONFIG);
    value &= 0b0000000000111000;
    value >>= 3;
    return (ina226_shuntConvTime_t)value;
}

ina226_mode_t INA226::getMode(void) {
    uint16_t value;
    value = mI2cIntf.readRegister16(INA226_REG_CONFIG);
    value &= 0b0000000000000111;
    return (ina226_mode_t)value;
}

void INA226::setMaskEnable(uint16_t mask) {
    mI2cIntf.writeRegister16(INA226_REG_MASKENABLE, mask);
}

uint16_t INA226::getMaskEnable(void) {
    return mI2cIntf.readRegister16(INA226_REG_MASKENABLE);
}

void INA226::enableShuntOverLimitAlert(void) {
    mI2cIntf.writeRegister16(INA226_REG_MASKENABLE, INA226_BIT_SOL);
}

void INA226::enableShuntUnderLimitAlert(void) {
    mI2cIntf.writeRegister16(INA226_REG_MASKENABLE, INA226_BIT_SUL);
}

void INA226::enableBusOvertLimitAlert(void) {
    mI2cIntf.writeRegister16(INA226_REG_MASKENABLE, INA226_BIT_BOL);
}

void INA226::enableBusUnderLimitAlert(void) {
    mI2cIntf.writeRegister16(INA226_REG_MASKENABLE, INA226_BIT_BUL);
}

void INA226::enableOverPowerLimitAlert(void) {
    mI2cIntf.writeRegister16(INA226_REG_MASKENABLE, INA226_BIT_POL);
}

void INA226::enableConversionReadyAlert(void) {
    writeRegister16(INA226_REG_MASKENABLE, INA226_BIT_CNVR);
}

void INA226::setBusVoltageLimit(float voltage) {
    uint16_t value = voltage / 0.00125;
    mI2cIntf.writeRegister16(INA226_REG_ALERTLIMIT, value);
}

void INA226::setShuntVoltageLimit(float voltage) {
    uint16_t value = voltage * 25000;
    mI2cIntf.writeRegister16(INA226_REG_ALERTLIMIT, value);
}

void INA226::setPowerLimit(float watts) {
    uint16_t value = watts / powerLSB;
    mI2cIntf.writeRegister16(INA226_REG_ALERTLIMIT, value);
}

void INA226::setAlertInvertedPolarity(bool inverted) {
    uint16_t temp = getMaskEnable();

    if (inverted) {
        temp |= INA226_BIT_APOL;
    } else {
        temp &= ~INA226_BIT_APOL;
    }

    setMaskEnable(temp);
}

void INA226::setAlertLatch(bool latch) {
    uint16_t temp = getMaskEnable();

    if (latch) {
        temp |= INA226_BIT_LEN;
    } else {
        temp &= ~INA226_BIT_LEN;
    }

    setMaskEnable(temp);
}

bool INA226::isMathOverflow(void) {
    return ((getMaskEnable() & INA226_BIT_OVF) == INA226_BIT_OVF);
}

bool INA226::isAlert(void) {
    return ((getMaskEnable() & INA226_BIT_AFF) == INA226_BIT_AFF);
}
